﻿using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace RemoteTestRunner.UnitTesting
{
    internal static class MultiAssertTestXmlRepository
    {
        internal static void Persist(MultiAssertTestClassHelper results, string outputPath)
        {
            // For an example of what this code creates see the SampleReportOutput.xml file in this project.
            var root = new XElement(
                "TestClassResults",
                new XAttribute("Name", results.TestClass));

            var functions = results.Functions.Select(f => f.TestMethodName).Distinct();

            // Build a list of methods for the current class.  
            // The list of methods will serve as an index to the tests in this class.  
            // This index will also contain a list of the unique input and asserts for each method.
            var methodsSummary = new XElement("TestMethodsSummary");

            foreach (var function in functions)
            {
                var methodSummary = new XElement(
                    "TestMethodSummary",
                    new XAttribute("Name", function));

                var firstFunction = results.Functions.First(f => f.TestMethodName == function);

                var inputsSummary = new XElement("InputsSummary");
                inputsSummary.Add(
                    from input in firstFunction.Inputs
                    select new XElement(
                        "InputSummary",
                        new XAttribute("Name", input.Name),
                        new XAttribute("Missing", input.Missing.ToString())
                        ));

                methodSummary.Add(inputsSummary);

                var assertsSummary = new XElement("AssertsSummary");
                assertsSummary.Add(
                    from assert in firstFunction.Asserts
                    select new XElement(
                        "AssertSummary",
                        new XAttribute("Name", assert.AssertName),
                        new XAttribute("Missing", assert.Missing.ToString())));

                methodSummary.Add(assertsSummary);

                methodsSummary.Add(methodSummary);
            }

            // List out all of the methods for the class.
            // Each method will contain a list of the inputs used in that method
            // as well as the asserts that where executed in that method.
            // Exceptions encountered during an assert will be listed within the assert.
            root.Add(methodsSummary);

            var testMethods = new XElement("TestMethods");

            foreach (var result in results.Functions)
            {
                var method = new XElement(
                    "TestMethod",
                    new XAttribute("Name", result.TestMethodName));

                // input values for the method
                var inputs = new XElement("Inputs");
                inputs.Add(
                    from input in result.Inputs
                    select new XElement(
                        "Input",
                        new XAttribute("Name", input.Name),
                        new XAttribute("Value", input.Value),
                        new XAttribute("Missing", input.Missing.ToString())));

                method.Add(inputs);

                // Asserts for the method.
                var asserts = new XElement("Asserts");

                foreach (var assert in result.Asserts)
                {
                    var assertResult =
                        new XElement(
                            "Assert",
                            new XAttribute("Name", assert.AssertName),
                            new XAttribute("Expected", assert.Expected),
                            new XAttribute("Actual", assert.Actual),
                            new XAttribute("TestPassed", assert.TestPassed.ToString()),
                            new XAttribute("Message", assert.Message),
                            new XAttribute("Missing", assert.Missing.ToString()));

                    // if exception data is included for the assert then add the exception details to the output.
                    if (assert.ResultException != null)
                    {
                        assertResult.Add(
                            new XElement(
                                "ErrorException",
                                new XAttribute("Message", assert.ResultException.Message),
                                new XAttribute("Full", assert.ResultException.ToString())));
                    }

                    asserts.Add(assertResult);
                }

                method.Add(asserts);
                testMethods.Add(method);
            }

            root.Add(testMethods);

            using (var xmlWriter = new XmlTextWriter(outputPath, Encoding.ASCII))
            {
                root.WriteTo(xmlWriter);
            }
        }
    }
}